﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Asol.Reporting.Support.XmlPersistor;
using Asol.Reporting.Support.Components;
using System.Windows.Forms;
using Asol.Reporting.Support;
using System.Drawing;

namespace Asol.Reporting.Green
{
    #region Settings : Konfigurace pro práci s Greenem ze samostatné aplikace
    /// <summary>
    /// Konfigurace pro práci s Greenem ze samostatné aplikace
    /// </summary>
    [PersistingDefault(PersistingDefaultMode.OnlyExplicit)]
    public class Settings : IXmlPersistNotify
    {
        #region Konstrukce
        public Settings()
        {
            this.ServerList = new List<SettingsServer>();
        }
        #endregion
        #region Public property
        /// <summary>
        /// Název XML souboru pro ukládání obsahu
        /// </summary>
        [PersistingEnabled(false)]
        public string SettingFileName { get; set; }
        /// <summary>
        /// Název aktivního serveru, je platný jen v době ukládání/načítání z XML
        /// </summary>
        [PersistingEnabled(true)]
        [PropertyName("ActiveServer")]
        private string ActiveServerName { get; set; }
        /// <summary>
        /// Soupis serverů
        /// </summary>
        [PersistingEnabled(true)]
        [PropertyName("servers")]
        [CollectionItemName("server")]
        public List<SettingsServer> ServerList { get; set; }
        /// <summary>
        /// Objekt aktivního serveru
        /// </summary>
        [PersistingEnabled(false)]
        public SettingsServer ActiveServer { get; set; }
        /// <summary>
        /// Soupis profilů aktivního serveru (ActiveServer.ProfileList).
        /// Pokud není vybrán server, je zde null.
        /// </summary>
        [PersistingEnabled(false)]
        public List<SettingsProfile> ProfileList { get { return (this.ActiveServer != null ? this.ActiveServer.ProfileList : null); } }
        /// <summary>
        /// Aktivní profil aktivního aktivního serveru (ActiveServer.ActiveProfile).
        /// Pokud není vybrán server, je zde null.
        /// </summary>
        [PersistingEnabled(false)]
        public SettingsProfile ActiveProfile
        {
            get { return (this.ActiveServer != null ? this.ActiveServer.ActiveProfile : null); }
            set { if (this.ActiveServer != null) this.ActiveServer.ActiveProfile = value; }
        }
        /// <summary>
        /// Connect string pro aktuální server a databázi
        /// </summary>
        [PersistingEnabled(false)]
        public System.Data.SqlClient.SqlConnectionStringBuilder ActiveConnectStringBuilder
        {
            get { return (this.ActiveServer != null ? this.ActiveServer.ActiveConnectStringBuilder : null); }
        }
        #endregion
        #region Podpora editace pomocí ComboEditBoxu
        /// <summary>
        /// Tato metoda zajistí editaci serverů
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public void ServerEditAction(object sender, ComboEditEventArgs args)
        {
            SettingsServer server = args.Data as SettingsServer;
            switch (args.Action)
            {
                case ComboEditActionType.Select:
                    if (server != null)
                    {
                        this.ActiveServer = server;
                        this.Update();
                        args.Commit = true;
                    }
                    break;

                case ComboEditActionType.CreateNew:
                    args.Data = new SettingsServer(this);
                    args.Commit = true;
                    break;
                case ComboEditActionType.CreateClone:
                    args.Data = new SettingsServer(this, server);
                    args.Commit = true;
                    break;
                case ComboEditActionType.ShowEdit:
                    args.Commit = DataEditForm.EditObject(server, args.CallerScreenBounds, Assistant.GetMessage("Aplikační server"));
                    break;
                case ComboEditActionType.UpdateExisting:
                    this.Update();
                    args.Commit = true;
                    break;
                case ComboEditActionType.UpdateNew:
                    if (server != null)
                    {
                        this.ServerList.Add(server);
                        this.Update();
                        args.Commit = true;
                    }
                    break;
                case ComboEditActionType.Delete:
                    if (server != null)
                    {
                        this.ServerList.Remove(server);
                        this.Update();
                        args.Commit = true;
                    }
                    break;
            }
        }
        /// <summary>
        /// Tato metoda zajistí editaci profilů aktivního serveru
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public void ProfileEditAction(object sender, ComboEditEventArgs args)
        {
            SettingsServer server = this.ActiveServer;
            if (server == null) return;

            SettingsProfile profile = args.Data as SettingsProfile;
            switch (args.Action)
            {
                case ComboEditActionType.Select:
                    if (profile != null)
                    {
                        this.ActiveProfile = profile;
                        this.Update();
                        args.Commit = true;
                    }
                    break;

                case ComboEditActionType.CreateNew:
                    args.Data = new SettingsProfile(server);
                    args.Commit = true;
                    break;
                case ComboEditActionType.CreateClone:
                    args.Data = new SettingsProfile(server, profile);
                    args.Commit = true;
                    break;
                case ComboEditActionType.ShowEdit:
                    args.Commit = DataEditForm.EditObject(profile, args.CallerScreenBounds, Assistant.GetMessage("Připojovací profil"));
                    break;
                case ComboEditActionType.UpdateExisting:
                    this.Update();
                    args.Commit = true;
                    break;
                case ComboEditActionType.UpdateNew:
                    if (profile != null)
                    {
                        this.ProfileList.Add(profile);
                        this.Update();
                        args.Commit = true;
                    }
                    break;
                case ComboEditActionType.Delete:
                    if (profile != null)
                    {
                        this.ProfileList.Remove(profile);
                        this.Update();
                        args.Commit = true;
                    }
                    break;
            }
        }
        #endregion
        #region Serializace
        /// <summary>
        /// Aktuální stav procesu XML persistence.
        /// Umožňuje persistovanému objektu reagovat na ukládání nebo na načítání dat.
        /// Do této property vkládá XmlPersistor hodnotu odpovídající aktuální situaci.
        /// Datová instance může v set accessoru zareagovat a například připravit data pro Save, 
        /// anebo dokončit proces Load (navázat si další data nebo provést přepočty a další reakce).
        /// </summary>
        [PersistingEnabled(false)]
        XmlPersistState IXmlPersistNotify.XmlPersistState
        {
            get { return _XmlPersistState; }
            set
            {
                switch (value)
                {
                    case XmlPersistState.SaveBegin:
                        this.XmlSaveBegin();
                        break;
                    case XmlPersistState.LoadDone:
                        this.XmlLoadDone();
                        break;
                }
                _XmlPersistState = value;
            }
        }
        /// <summary>
        /// Před ukládáním dat
        /// </summary>
        private void XmlSaveBegin()
        {
            // Do ActiveServerName připravím název aktivního serveru, odtud se uloží do XML:
            this.ActiveServerName = (this.ActiveServer == null || this.ActiveServer.ServerName == null ? null : this.ActiveServer.ServerName);
        }
        /// <summary>
        /// Voláno po načtení dat z XML
        /// </summary>
        private void XmlLoadDone()
        {
            this.ServerList.RemoveAll(s => s == null);
            foreach (SettingsServer server in this.ServerList)
                server.Owner = this;
            // Podle názvu serveru uloženého v ActiveServerName najdu odpovídající objekt:
            this.ActiveServer = (this.ActiveServerName == null ? null : this.ServerList.FirstOrDefault(s => String.Equals(this.ActiveServerName, s.ServerName, StringComparison.Ordinal)));
        }
        XmlPersistState _XmlPersistState;
        #endregion
        #region Save & Load
        /// <summary>
        /// Vytvoří, naplní a vrátí nastavení z uvedeného XML souboru
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static Settings Create(string fileName)
        {
            Settings settings = null;
            if (!String.IsNullOrEmpty(fileName) && System.IO.File.Exists(fileName))
                settings = XmlPersist.Deserialize<Settings>(fileName);
            if (settings == null)
                settings = new Settings();

            settings.SettingFileName = fileName;
            return settings;
        }
        public void Update()
        {
            this.Update(this.SettingFileName);
        }
        public void Update(string fileName)
        {
            if (String.IsNullOrEmpty(fileName))
                throw new Reporting.Support.DevException("Nelze uložit nastavení, není určeno jméno souboru.");
            XmlPersistArgs args = new XmlPersistArgs();
            args.XmlFile = fileName;
            XmlPersist.Serialize(this, args);
        }
        #endregion
    }
    #endregion
    #region SettingsServer : Data jednoho aplikačního serveru
    /// <summary>
    /// Data jednoho aplikačního serveru
    /// </summary>
    public class SettingsServer : IEditableObject, IXmlPersistNotify
    {
        #region Konstrukce dat, klonování
        /// <summary>
        /// Konstruktor
        /// </summary>
        public SettingsServer()
        {
            this.Owner = null;
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        public SettingsServer(Settings owner)
        {
            this.Owner = owner;
            this.ProfileList = new List<SettingsProfile>();
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        public SettingsServer(Settings owner, SettingsServer source)
        {
            this.Owner = owner;
            this.ProfileList = new List<SettingsProfile>();
            SettingsServer.Clone(source, this);
        }
        /// <summary>
        /// Přenese obsah objektu source do objektu target.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        public static void Clone(SettingsServer source, SettingsServer target)
        {
            if (source == null || target == null) return;

            target.ServerName = source.ServerName;
            target.BinaryPath = source.BinaryPath;
            target.ReportPath = source.ReportPath;
            target.ConfigFile = source.ConfigFile;
            target.ResourcePath = source.ResourcePath;
            target.ProfileList = new List<SettingsProfile>();
            foreach (SettingsProfile p in source.ProfileList)
                target.ProfileList.Add(new SettingsProfile(target, p));
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return (this.ServerName == null ? " (nepojmenovaný)" : this.ServerName);
        }
        #endregion
        #region Public property
        /// <summary>Vlastník instance</summary>
        [PersistingEnabled(false)]
        public Settings Owner { get; internal set; }
        /// <summary>Název serveru</summary>
        [PropertyName("Name", 1)]
        public string ServerName { get; set; }
        /// <summary>Název aktivního profilu, je platný jen v době ukládání/načítání z XML</summary>
        [PropertyName("ActiveProfile", 2)]
        private string ActiveProfileName { get; set; }
        /// <summary>Adresář binárních knihoven (dll)</summary>
        [PropertyName("Binaries", 3)]
        public string BinaryPath { get; set; }
        /// <summary>Adresář reportů</summary>
        [PropertyName("Reports", 4)]
        public string ReportPath { get; set; }
        /// <summary>Adresář se soubore, Noris.resources</summary>
        [PropertyName("Resources", 5)]
        public string ResourcePath { get; set; }
        /// <summary>Soubor s konfigurací Noris.config</summary>
        [PropertyName("Config", 6)]
        public string ConfigFile { get; set; }
        /// <summary>Seznam db profilů</summary>
        [PropertyName("databases")]
        [CollectionItemName("database")]
        public List<SettingsProfile> ProfileList { get; private set; }
        /// <summary>
        /// Objekt aktivního profilu
        /// </summary>
        [PersistingEnabled(false)]
        public SettingsProfile ActiveProfile { get; set; }
        /// <summary>
        /// Connect string pro aktuální profil
        /// </summary>
        [PersistingEnabled(false)]
        public System.Data.SqlClient.SqlConnectionStringBuilder ActiveConnectStringBuilder
        {
            get { return (this.ActiveProfile != null ? this.ActiveProfile.ActiveConnectStringBuilder : null); }
        }
        #endregion
        #region Serializace
        /// <summary>
        /// Aktuální stav procesu XML persistence.
        /// Umožňuje persistovanému objektu reagovat na ukládání nebo na načítání dat.
        /// Do této property vkládá XmlPersistor hodnotu odpovídající aktuální situaci.
        /// Datová instance může v set accessoru zareagovat a například připravit data pro Save, 
        /// anebo dokončit proces Load (navázat si další data nebo provést přepočty a další reakce).
        /// </summary>
        [PersistingEnabled(false)]
        XmlPersistState IXmlPersistNotify.XmlPersistState
        {
            get { return _XmlPersistState; }
            set
            {
                switch (value)
                {
                    case XmlPersistState.SaveBegin:
                        this.XmlSaveBegin();
                        break;
                    case XmlPersistState.LoadDone:
                        this.XmlLoadDone();
                        break;
                }
                _XmlPersistState = value;
            }
        }
        /// <summary>
        /// Před ukládáním dat
        /// </summary>
        private void XmlSaveBegin()
        {
            // Do ActiveProfileName připravím název aktivního profilu, odtud se uloží do XML:
            this.ActiveProfileName = (this.ActiveProfile == null || this.ActiveProfile.ProfileName == null ? null : this.ActiveProfile.ProfileName);
        }
        /// <summary>
        /// Voláno po načtení dat z XML
        /// </summary>
        private void XmlLoadDone()
        {
            this.ProfileList.RemoveAll(p => p == null);
            foreach (SettingsProfile profile in this.ProfileList)
                profile.Owner = this;
            // Podle názvu profilu uloženého v ActiveProfileName najdu odpovídající objekt:
            this.ActiveProfile = (this.ActiveProfileName == null ? null : this.ProfileList.FirstOrDefault(p => String.Equals(this.ActiveProfileName, p.ProfileName, StringComparison.Ordinal)));
        }
        XmlPersistState _XmlPersistState;
        #endregion
        #region implementace IEditableObject + vizuální control IEditableControl
        Control IEditableObject.CreateControl()
        {
            return new SettingsServerControl(this);
        }
        /// <summary>
        /// Vizuální control pro editaci dat jednoho serveru
        /// </summary>
        protected class SettingsServerControl : EditableControl<SettingsServer>, IEditableControl
        {
            public SettingsServerControl(SettingsServer data) 
                : base(data)
            { }
            #region overrides EditableControl : create control
            protected override void InitializeControls()
            {
                int y = 4;
                int ys = 24;
                int yl = 4;
                int xl = 8;
                int wl = 88;
                int hl = 18;

                int xt = yl + wl + 4;
                int wt = 250;
                Control lastControl = null;

                this.ServerNameLabel = new Label() { Left = xl, Top = y + yl, Width = wl, Height = hl, Text = "Název:", TextAlign = System.Drawing.ContentAlignment.TopRight, AutoSize = false };
                this.Controls.Add(this.ServerNameLabel);
                this.ServerNameText = new TextBox() { Left = xt, Top = y, Width = wt };
                this.ServerNameText.TextChanged += this.AnyControlChanged;
                this.Controls.Add(this.ServerNameText);
                y += ys;

                this.BinaryPathLabel = new Label() { Left = xl, Top = y + yl, Width = wl, Text = "Adresář /bin (.dll):", TextAlign = System.Drawing.ContentAlignment.TopRight, AutoSize = false };
                this.Controls.Add(this.BinaryPathLabel);
                this.BinaryPathText = new FileSelectPanel() { Left = xt, Top = y, Width = wt, SelectMode = PathSelectMode.GetDirectoryFromFile };
                this.BinaryPathText.SelectedPathChanged += this.AnyControlChanged;
                this.Controls.Add(this.BinaryPathText);
                y += ys;

                this.ReportPathLabel = new Label() { Left = xl, Top = y + yl, Width = wl, Text = "Adresář reportů:", TextAlign = System.Drawing.ContentAlignment.TopRight, AutoSize = false };
                this.Controls.Add(this.ReportPathLabel);
                this.ReportPathText = new FileSelectPanel() { Left = xt, Top = y, Width = wt, SelectMode = PathSelectMode.GetDirectoryFromFile };
                this.ReportPathText.SelectedPathChanged += this.AnyControlChanged;
                this.Controls.Add(this.ReportPathText);
                lastControl = this.ReportPathText;
                y += ys;

                this.AutoScroll = true;
                this.SizeChanged += new EventHandler(_SizeChanged);
                this.Size = new System.Drawing.Size(lastControl.Right + 12, lastControl.Bottom + 8);
            }
            private Label ServerNameLabel;
            private TextBox ServerNameText;
            private Label BinaryPathLabel;
            private FileSelectPanel BinaryPathText;
            private Label ReportPathLabel;
            private FileSelectPanel ReportPathText;
            void _SizeChanged(object sender, EventArgs e)
            {
                this._SizeChanged();
            }
            void _SizeChanged()
            {
                int r = this.ClientSize.Width - 8;
                this.ServerNameText.Width = r - this.ServerNameText.Left;
                this.BinaryPathText.Width = r - this.BinaryPathText.Left;
                this.ReportPathText.Width = r - this.ReportPathText.Left;
            }
            #endregion
            #region overrides EditableControl: show and gather data
            protected override void ShowData(SettingsServer data)
            {
                this.ServerNameText.Text = data.ServerName;
                this.BinaryPathText.SelectedPath = data.BinaryPath;
                this.ReportPathText.SelectedPath = data.ReportPath;
            }
            protected override void GatherData(SettingsServer data)
            {
                data.ServerName = this.ServerNameText.Text;
                data.BinaryPath = this.BinaryPathText.SelectedPath;
                data.ReportPath = this.ReportPathText.SelectedPath;
            }
            #endregion
        }
        #endregion
    }
    #endregion
    #region SettingsProfile : Data jednoho připojovacího profilu
    /// <summary>
    /// Data jednoho připojovacího profilu
    /// </summary>
    [PropertyName("db")]
    public class SettingsProfile : IEditableObject, IXmlPersistNotify
    {
        #region Konstrukce dat, klonování
        /// <summary>
        /// Konstruktor
        /// </summary>
        public SettingsProfile()
        {
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        public SettingsProfile(SettingsServer owner)
        {
            this.Owner = owner;
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        public SettingsProfile(SettingsServer owner, SettingsProfile source)
        {
            this.Owner = owner;
            SettingsProfile.Clone(source, this);
        }
        /// <summary>
        /// Přenese obsah objektu source do objektu target.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="target"></param>
        public static void Clone(SettingsProfile source, SettingsProfile target)
        {
            if (source == null || target == null) return;

            target.ProfileName = source.ProfileName;
            target.DbServer = source.DbServer;
            target.DbName = source.DbName;
            target.IntegratedSecurity = source.IntegratedSecurity;
            target.DbLoginName = source.DbLoginName;
            target.DbLoginPassword = source.DbLoginPassword;
            target.DbConnectionName = source.DbConnectionName;
            target.GreenUserName = source.GreenUserName;
            target.GreenPassword = source.GreenPassword;
        }
        public override string ToString()
        {
            return this.ProfileName;
        }
        #endregion
        #region Public property
        /// <summary>Vlastník instance = server</summary>
        [PersistingEnabled(false)]
        public SettingsServer Owner { get; internal set; }
        [PropertyName("Name", 1)]
        public string ProfileName { get; set; }
        [PropertyName("Server", 2)]
        public string DbServer { get; set; }
        [PropertyName("Db", 3)]
        public string DbName { get; set; }
        [PropertyName("IntegratedSecurity", 4)]
        public bool IntegratedSecurity { get; set; }
        [PropertyName("DbLogin", 5)]
        public string DbLoginName { get; set; }
        [PropertyName("DbPassword", 6)]
        public string DbLoginPasswordCrypted { get; set; }
        [PersistingEnabled(false)]
        public string DbLoginPassword { get; set; }
        [PropertyName("ConnectionName", 7)]
        public string DbConnectionName { get; set; }
        [PropertyName("GreenUser")]
        public string GreenUserName { get; set; }
        [PropertyName("GreenPassword", 8)]
        public string GreenPasswordCrypted { get; set; }
        [PersistingEnabled(false)]
        public string GreenPassword { get; set; }
        /// <summary>
        /// Connect string pro tento profil
        /// </summary>
        [PersistingEnabled(false)]
        public System.Data.SqlClient.SqlConnectionStringBuilder ActiveConnectStringBuilder
        {
            get
            {
                System.Data.SqlClient.SqlConnectionStringBuilder csb = new System.Data.SqlClient.SqlConnectionStringBuilder();
                
                if (!String.IsNullOrEmpty(this.DbServer))
                    csb.DataSource = this.DbServer;
                csb.IntegratedSecurity = this.IntegratedSecurity;
                if (!this.IntegratedSecurity)
                {
                    if (!String.IsNullOrEmpty(this.DbLoginName))
                    {
                        csb.UserID = this.DbLoginName;
                        if (!String.IsNullOrEmpty(this.DbLoginPassword))
                            csb.Password = this.DbLoginPassword;
                    }
                }
                if (!String.IsNullOrEmpty(this.DbName))
                    csb.InitialCatalog = this.DbName;
                if (!String.IsNullOrEmpty(this.DbConnectionName))
                    csb.ApplicationName = this.DbConnectionName;

                return csb;
            }
        }
        #endregion
        #region Serializace
        /// <summary>
        /// Aktuální stav procesu XML persistence.
        /// Umožňuje persistovanému objektu reagovat na ukládání nebo na načítání dat.
        /// Do této property vkládá XmlPersistor hodnotu odpovídající aktuální situaci.
        /// Datová instance může v set accessoru zareagovat a například připravit data pro Save, 
        /// anebo dokončit proces Load (navázat si další data nebo provést přepočty a další reakce).
        /// </summary>
        [PersistingEnabled(false)]
        XmlPersistState IXmlPersistNotify.XmlPersistState
        {
            get { return _XmlPersistState; }
            set
            {
                switch (value)
                {
                    case XmlPersistState.SaveBegin:
                        this.XmlSaveBegin();
                        break;
                    case XmlPersistState.LoadDone:
                        this.XmlLoadDone();
                        break;
                }
                _XmlPersistState = value;
            }
        }
        /// <summary>
        /// Před ukládáním dat
        /// </summary>
        private void XmlSaveBegin()
        {
            // Do DbLoginPasswordCrypted připravím zakryptované heslo, odtud se uloží do XML (čitelná property DbLoginPassword se nepersistuje):
            this.DbLoginPasswordCrypted = (this.IntegratedSecurity ? null : Crypt.EncryptToString(this.DbLoginPassword == null ? "" : this.DbLoginPassword));
            this.GreenPasswordCrypted = (String.IsNullOrEmpty(this.GreenUserName) ? null : Crypt.EncryptToString(this.GreenPassword == null ? "" : this.GreenPassword));
        }
        /// <summary>
        /// Voláno po načtení dat z XML
        /// </summary>
        private void XmlLoadDone()
        {
            // Provedu dekryptování hesla:
            this.DbLoginPassword = (String.IsNullOrEmpty(this.DbLoginPasswordCrypted) ? "" : Crypt.Decrypt(this.DbLoginPasswordCrypted));
            this.GreenPassword = (String.IsNullOrEmpty(this.GreenPasswordCrypted) ? "" : Crypt.Decrypt(this.GreenPasswordCrypted));
        }
        XmlPersistState _XmlPersistState;
        #endregion
        #region implementace IEditableObject + vizuální control IEditableControl
        Control IEditableObject.CreateControl()
        {
            return new SettingsProfileControl(this);
        }
        /// <summary>
        /// Vizuální control pro editaci dat jednoho serveru
        /// </summary>
        protected class SettingsProfileControl : EditableControl<SettingsProfile>, IEditableControl
        {
            public SettingsProfileControl(SettingsProfile data)
                : base(data)
            { }
            protected override void Dispose(bool disposing)
            {
                this.BackThreadStop();
                base.Dispose(disposing);
            }
            #region overrides EditableControl : create control
            protected override void InitializeControls()
            {
                this.BackThreadInit();

                int y = 4;
                int ys = 21;
                int yl = 4;
                int xl = 8;
                int wl = 110;
                int hl = 15;

                int xt = yl + wl + 4;
                int wt = 250;
                Control lastControl = null;

                this.ProfileNameLabel = new Label() { Left = xl, Top = y + yl, Width = wl, Height = hl, Text = "Název:", TextAlign = System.Drawing.ContentAlignment.TopRight, AutoSize = false };
                this.Controls.Add(this.ProfileNameLabel);
                this.ProfileNameText = new TextBox() { Left = xt, Top = y, Width = wt };
                this.ProfileNameText.TextChanged += this.AnyControlChanged;
                this.Controls.Add(this.ProfileNameText);
                y += ys;

                this.DbServerLabel = new Label() { Left = xl, Top = y + yl, Width = wl, Height = hl, Text = "Databázový server:", TextAlign = System.Drawing.ContentAlignment.TopRight, AutoSize = false };
                this.Controls.Add(this.DbServerLabel);
                this.DbServerText = new TextBox() { Left = xt, Top = y, Width = wt };
                this.DbServerText.TextChanged += this.ConnectChanged;
                this.Controls.Add(this.DbServerText);
                y += ys;

                this.DbLoginModeLabel = new Label() { Left = xl, Top = y + yl, Width = wl, Height = hl, Text = "Režim přihlašování:", TextAlign = System.Drawing.ContentAlignment.TopRight, AutoSize = false };
                this.Controls.Add(this.DbLoginModeLabel);
                this.DbLoginModeCombo = new ComboBox() { Left = xt, Top = y, Width = wt, DropDownStyle = ComboBoxStyle.DropDownList };
                this.DbLoginModeCombo.Items.Add(Assistant.GetMessage("Windows autentikace"));
                this.DbLoginModeCombo.Items.Add(Assistant.GetMessage("SQL server login"));
                this.DbLoginModeCombo.SelectedIndexChanged += this.ConnectModeChanged;
                this.Controls.Add(this.DbLoginModeCombo);
                y += ys + 1;

                this.DbUserNameLabel = new Label() { Left = xl, Top = y + yl, Width = wl, Height = hl, Text = "SQL login a heslo:", TextAlign = System.Drawing.ContentAlignment.TopRight, AutoSize = false };
                this.Controls.Add(this.DbUserNameLabel);
                this.DbUserNameText = new TextBox() { Left = xt, Top = y, Width = wt };
                this.DbUserNameText.TextChanged += this.ConnectChanged;
                this.Controls.Add(this.DbUserNameText);
                this.DbUserPassText = new TextBox() { Left = xt, Top = y, Width = wt, PasswordChar = '×' };
                this.DbUserPassText.TextChanged += this.ConnectChanged;
                this.Controls.Add(this.DbUserPassText);
                y += ys;

                this.DbConnectLabel = new Label() { Left = xl, Top = y + yl, Width = wl, Height = hl, Text = "Stav připojení:", TextAlign = System.Drawing.ContentAlignment.TopRight, AutoSize = false };
                this.Controls.Add(this.DbConnectLabel);
                this.DbConnectProgress = new ProgressBar() { Left = xt, Top = y + yl + 2, Width = wt, Height = hl - 7, Style = ProgressBarStyle.Marquee, MarqueeAnimationSpeed = 200, Visible = false };
                this.Controls.Add(this.DbConnectProgress);
                this.DbConnectInfo = new Label() { Left = xt, Top = y + yl, Width = wt, Height = hl, Text = "...", TextAlign = System.Drawing.ContentAlignment.TopCenter, AutoSize = false, Visible = true };
                this.Controls.Add(this.DbConnectInfo);
                y += ys;

                this.DbDatabaseLabel = new Label() { Left = xl, Top = y + yl, Width = wl, Height = hl, Text = "Databáze:", TextAlign = System.Drawing.ContentAlignment.TopRight, AutoSize = false };
                this.Controls.Add(this.DbDatabaseLabel);
                this.DbDatabaseCombo = new ComboBox() { Left = xt, Top = y, Width = wt, DropDownStyle = ComboBoxStyle.DropDownList };
                this.DbDatabaseCombo.SelectedIndexChanged += this.DbNameChanged;
                this.Controls.Add(this.DbDatabaseCombo);
                y += ys + 1;

                lastControl = this.DbDatabaseCombo;

                this.AutoScroll = true;
                this.SizeChanged += new EventHandler(_SizeChanged);
                this.Size = new System.Drawing.Size(lastControl.Right + 12, lastControl.Bottom + 8);
            }
            private Label ProfileNameLabel;
            private TextBox ProfileNameText;
            private Label DbServerLabel;
            private TextBox DbServerText;
            private Label DbLoginModeLabel;
            private ComboBox DbLoginModeCombo;
            private Label DbUserNameLabel;
            private TextBox DbUserNameText;
            private TextBox DbUserPassText;
            private Label DbConnectLabel;
            private ProgressBar DbConnectProgress;
            private Label DbConnectInfo;
            private Label DbDatabaseLabel;
            private ComboBox DbDatabaseCombo;
            private void ConnectModeChanged(object sender, EventArgs args)
            {
                bool sqlLogin = (this.DbLoginModeCombo.SelectedIndex == 1);
                this.DbUserNameText.Enabled = sqlLogin;
                this.DbUserPassText.Enabled = sqlLogin;
                this.ConnectChanged(sender, args);
            }
            private void DbNameChanged(object sender, EventArgs args)
            {
                if (!this.DbNameChangedSkip)
                {
                    this.DbDatabaseName = (this.DbDatabaseCombo.SelectedItem == null ? null : this.DbDatabaseCombo.SelectedItem.ToString());
                    this.AnyControlChanged(sender, args);
                }
            }
            private void ConnectChanged(object sender, EventArgs args)
            {
                string sqlServer= this.DbServerText.Text;
                bool integratedSecurity = (this.DbLoginModeCombo.SelectedIndex == 0);
                string dbLogin = (integratedSecurity ? null : this.DbUserNameText.Text);
                string dbPassword = (integratedSecurity ? null : this.DbUserPassText.Text);
                this.BackThreadActivate(sqlServer, integratedSecurity, dbLogin, dbPassword);
                this.AnyControlChanged(sender, args);
            }
            void _SizeChanged(object sender, EventArgs e)
            {
                this._SizeChanged();
            }
            void _SizeChanged()
            {
                int r = this.ClientSize.Width - 8;
                this.ProfileNameText.Width = r - this.ProfileNameText.Left;
                this.DbServerText.Width = r - this.DbServerText.Left;
                this.DbLoginModeCombo.Width = r - this.DbLoginModeCombo.Left;
                int npw = r - this.DbUserNameText.Left;
                int ntw = (npw - 2) / 2;
                this.DbUserNameText.Width = ntw;
                this.DbUserPassText.Left = r - ntw;
                this.DbUserPassText.Width = ntw;
                this.DbConnectProgress.Width = r - this.DbConnectProgress.Left;
                this.DbConnectInfo.Width = r - this.DbConnectInfo.Left;
                this.DbDatabaseCombo.Width = r - this.DbDatabaseCombo.Left;
            }
            private string DbDatabaseName;
            private bool DbNameChangedSkip;
            #endregion
            #region overrides EditableControl: show and gather data
            protected override void ShowData(SettingsProfile data)
            {
                this.ProfileNameText.Text = data.ProfileName;
                this.DbServerText.Text = data.DbServer;
                this.DbLoginModeCombo.SelectedIndex = (data.IntegratedSecurity ? 0 : 1);
                this.DbUserNameText.Text = data.DbLoginName;
                this.DbUserPassText.Text = data.DbLoginPassword;
                this.DbDatabaseName = data.DbName;

                this.BackReset.Set();          // Tím spustím connect do databáze okamžitě
            }
            protected override void GatherData(SettingsProfile data)
            {
                data.ProfileName = this.ProfileNameText.Text;
                data.DbServer = this.DbServerText.Text;
                bool intSec = (this.DbLoginModeCombo.SelectedIndex == 0);
                data.IntegratedSecurity = intSec;
                data.DbLoginName = (intSec ? null : this.DbUserNameText.Text);
                data.DbLoginPassword = (intSec ? null : this.DbUserPassText.Text);
                data.DbName = this.DbDatabaseName;
            }
            #endregion
            #region Thread na pozadí - řeší zkušební připojení k danému SQL serveru
            /// <summary>
            /// Vytvoří thread na pozadí a spustí jej 
            /// (vlákno ihned přejde do stavu spánku, čeká na provozní budíček this.BackReset).
            /// </summary>
            private void BackThreadInit()
            {
                this.BackReset = new System.Threading.AutoResetEvent(false);
                this.BackThread = new System.Threading.Thread(this.BackThreadMethod);
                this.BackThread.Start();
            }
            /// <summary>
            /// Ukončí běh threadu na pozadí
            /// </summary>
            private void BackThreadStop()
            {
                this.BackThreadEnd = true;
                this.BackReset.Set();
            }
            /// <summary>
            /// Předá dané parametry databázového spojení do metody na pozadí, a aktivuje ji k otestování.
            /// Vlastní test bude spuštěn až za 2.5 sekundy, pokud do té doby nebude požadavek na spojení změněn.
            /// Pokud bude aktivace vyvolána v době, kdy test běží, pak po doběhnutí současného testu se provede 2.5 sec pauza 
            /// a po ní se spustí test s novými parametry.
            /// </summary>
            /// <param name="sqlServer"></param>
            /// <param name="integratedSecurity"></param>
            /// <param name="dbLogin"></param>
            /// <param name="dbPassword"></param>
            private void BackThreadActivate(string sqlServer, bool integratedSecurity, string dbLogin, string dbPassword)
            {
                System.Data.SqlClient.SqlConnectionStringBuilder csb = new System.Data.SqlClient.SqlConnectionStringBuilder();
                csb.DataSource = sqlServer;
                csb.IntegratedSecurity = integratedSecurity;
                if (!integratedSecurity)
                {
                    csb.UserID = (!String.IsNullOrEmpty(dbLogin) ? dbLogin : "");
                    if (!String.IsNullOrEmpty(dbPassword))
                        csb.Password = dbPassword;
                }
                this.BackThreadConnectString = csb.ConnectionString;
                this.ShowProgress(ShowProgressState.Waiting, "", false);
                this.BackReset.Set();
            }
            /// <summary>
            /// Výkonná smyčka threadu na pozadí: čeká na probuzení (this.BackReset.WaitOne()),
            /// po probuzení si načte testovací connect string (this.BackThreadConnectString),
            /// otestuje s ním spojení na databázi, 
            /// </summary>
            private void BackThreadMethod()
            {
                string connString1 = this.BackThreadConnectString;       // Na začátku je ConnectString null, takže spadnu do trvalého čekání na signál
                while (!this.BackThreadEnd)
                {
                    if (String.IsNullOrEmpty(connString1))
                        this.BackReset.WaitOne();                        // Pokud nejsou data, čekám stále (až někdo zazvoní)
                    else
                        this.BackReset.WaitOne(2500);                    // Pokud jsou data, čekám jen 2.5 sekundy (když nikdo nezazvoní, skončím s čekáním)
                    this.BackReset.Reset();
                    string connString2 = this.BackThreadConnectString;   // Pokud došlo ke změně během 2.5 sec, pak nebude probíhat test, ale další 2.5 sec čekání

                    if (this.BackThreadEnd)
                        break;

                    if (!String.IsNullOrEmpty(connString1) && !String.IsNullOrEmpty(connString2) && connString1 == connString2)
                        this.BackThreadConnectTest(connString1);

                    connString1 = connString2;
                }
            }
            /// <summary>
            /// Pokusí se připojit k SQL serveru s pomocí daného stringu.
            /// Běží na pozadí: controly musí invokovat.
            /// </summary>
            /// <param name="connString"></param>
            private void BackThreadConnectTest(string connString)
            {
                System.Data.SqlClient.SqlConnectionStringBuilder csb = new System.Data.SqlClient.SqlConnectionStringBuilder(connString);

                if (!String.IsNullOrEmpty(csb.DataSource))
                {
                    this.ShowProgress(ShowProgressState.Connecting, "", true);
                    string errMsg = null;
                    List<string> databaseList = this.BackThreadConnectLoadDatabases(csb, ref errMsg);
                    if (databaseList != null)
                    {
                        this.ShowProgress(ShowProgressState.Success, "OK", true);
                        this.FillDatabases(databaseList);
                    }
                    else
                        this.ShowProgress(ShowProgressState.Error, errMsg, true);
                }
                if (String.Equals(this.BackThreadConnectString, connString, StringComparison.InvariantCulture))
                    this.BackThreadConnectString = null;
            }
            /// <summary>
            /// Pokusí se načíst soupis databází
            /// </summary>
            /// <param name="csb"></param>
            /// <param name="errMsg"></param>
            /// <returns></returns>
            private List<string> BackThreadConnectLoadDatabases(System.Data.SqlClient.SqlConnectionStringBuilder csb, ref string errMsg)
            {
                csb.InitialCatalog = "master";
                csb.ConnectTimeout = 10;
                string sql = "SELECT name FROM sys.databases WHERE name not in ('master','tempdb','model','msdb','ReportServer','ReportServerTempDB') ORDER BY name";
                Support.Data.Table table = DbConnect.LoadTable(sql, csb.ConnectionString);

                try
                {
                    table = App.LoadTable(csb, sql);
                }
                catch (Exception exc)
                {
                    errMsg = exc.Message;
                    return null;
                }
                return (table != null ? table.Rows.Convert<string>(r => r[0]) : null);
            }
            /// <summary>
            /// Zobrazí stav procesu na pozadí
            /// </summary>
            /// <param name="state"></param>
            /// <param name="message"></param>
            /// <param name="fromBack"></param>
            private void ShowProgress(ShowProgressState state, string message, bool fromBack)
            {
                if (!this.IsHandleCreated) return;
                if (fromBack || this.InvokeRequired)
                {
                    this.BeginInvoke(new Action<ShowProgressState, string, bool>(this.ShowProgress), state, message, false);
                }
                else
                {
                    switch (state)
                    {
                        case ShowProgressState.None:
                            this.DbConnectProgress.Visible = false;
                            this.DbConnectInfo.Visible = false;
                            this.Refresh();
                            break;
                        case ShowProgressState.Waiting:
                            this.DbConnectProgress.MarqueeAnimationSpeed = 750;
                            this.DbConnectProgress.BackColor = Color.LightGray;
                            this.DbConnectProgress.Visible = true;
                            this.DbConnectInfo.Visible = false;
                            this.DbConnectProgress.Refresh();
                            this.Refresh();
                            break;
                        case ShowProgressState.Connecting:
                            this.DbConnectProgress.MarqueeAnimationSpeed = 200;
                            this.DbConnectProgress.BackColor = Color.LightGreen;
                            this.DbConnectProgress.Visible = true;
                            this.DbConnectInfo.Visible = false;
                            this.DbConnectProgress.Refresh();
                            this.Refresh();
                            break;
                        case ShowProgressState.Success:
                            this.DbConnectProgress.Visible = false;
                            this.DbConnectInfo.BackColor = Color.LightGreen;
                            this.DbConnectInfo.Text = (!String.IsNullOrEmpty(message) ? message : "");
                            this.DbConnectInfo.Visible = true;
                            this.Refresh();
                            break;
                        case ShowProgressState.Error:
                            this.DbConnectProgress.Visible = false;
                            this.DbConnectInfo.BackColor = Color.LightSalmon;
                            this.DbConnectInfo.Text = (!String.IsNullOrEmpty(message) ? message : "");
                            this.DbConnectInfo.Visible = true;
                            this.Refresh();
                            break;
                    }
                }
            }
            /// <summary>
            /// Naplní seznam databází, pokusí se přednastavit databázi this.DbDatabaseName
            /// </summary>
            /// <param name="databaseList"></param>
            private void FillDatabases(List<string> databaseList)
            {
                if (!this.IsHandleCreated) return;
                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new Action<List<string>>(this.FillDatabases), databaseList);
                }
                else
                {
                    bool skip = this.DbNameChangedSkip;
                    try
                    {
                        this.DbNameChangedSkip = true;
                        this.DbDatabaseCombo.Items.Clear();
                        this.DbDatabaseCombo.Items.AddRange(databaseList.Where(db => !String.IsNullOrEmpty(db)).ToArray());
                        if (!String.IsNullOrEmpty(this.DbDatabaseName))
                        {
                            this.DbDatabaseCombo.SelectedIndex = this.DbDatabaseCombo.Items.IndexOf(this.DbDatabaseName);
                        }
                    }
                    finally
                    {
                        this.DbNameChangedSkip = skip;
                    }
                }
            }
            /// <summary>
            /// Stav procesu na pozadí
            /// </summary>
            private enum ShowProgressState
            {
                /// <summary>Neběží, není ani progres, ani info</summary>
                None,
                /// <summary>Čekáme na test připojení</summary>
                Waiting,
                /// <summary>Probíhá test připojení</summary>
                Connecting,
                /// <summary>Test připojení skončen OK</summary>
                Success,
                /// <summary>Test připojení skončen s chybou</summary>
                Error
            }
            /// <summary>
            /// Thread na pozadí
            /// </summary>
            private System.Threading.Thread BackThread;
            /// <summary>
            /// Budíček threadu na pozadí
            /// </summary>
            private System.Threading.AutoResetEvent BackReset;
            /// <summary>
            /// Požadavek na konec threadu na pozadí
            /// </summary>
            private bool BackThreadEnd;
            /// <summary>
            /// Connect string, pro který má proběhnout pokus o připojení
            /// </summary>
            private string BackThreadConnectString;
            #endregion
        }
        #endregion
    }
    #endregion
}
